package com.xingu.xg.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xingu.xg.common.consts.MenuDescription;
import com.xingu.xg.common.entity.query.CopyNodeQuery;
import com.xingu.xg.common.entity.query.DeleteNodeQuery;
import com.xingu.xg.common.enums.ErrorEnums;
import com.xingu.xg.common.entity.query.AddNodeQuery;
import com.xingu.xg.common.utils.AssertUtils;
import com.xingu.xg.mapper.TreeMenuMapper;
import com.xingu.xg.mapper.pojo.TreeMenuDo;
import com.xingu.xg.service.TreeMenuService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class TreeMenuServiceImpl extends ServiceImpl<TreeMenuMapper, TreeMenuDo> implements TreeMenuService {

    @Resource
    private TreeMenuMapper treeMenuMapper;

    @Resource
    private PlatformTransactionManager transactionManager;

    @Resource
    private TransactionDefinition transactionDefinition;

    @Override
    public List<TreeMenuDo> queryTreeMenuList() {
        //查看所有节点
        List<TreeMenuDo> treeMenuDos = treeMenuMapper.selectList(new LambdaQueryWrapper<>());
        Map<Long, TreeMenuDo> parentMenus = treeMenuDos.stream()
                .collect(Collectors.toMap(TreeMenuDo::getId, node -> node));

        //组装同级结构
        List<TreeMenuDo> treeMenus = new ArrayList<>();
        for (TreeMenuDo treeMenuDo : treeMenuDos) {
            if ("0".equals(treeMenuDo.getHeadFlag())) {
                //遍历该菜单的同级菜单
                List<TreeMenuDo> menuDos = querySameLevelMenu(treeMenuDo, parentMenus);
                for (TreeMenuDo menuDo : menuDos) {
                    if (menuDo.getParentId() != null) {
                        //把该菜单和该菜单的同级菜单添加到该菜单的父菜单
                        TreeMenuDo parentMenu = parentMenus.get(menuDo.getParentId());
                        List<TreeMenuDo> menus = parentMenu.getTreeMenuDos();
                        menus.addAll(menuDos);
                    } else if (!new HashSet<>(treeMenus).containsAll(menuDos)) {
                        treeMenus.addAll(menuDos);
                    }
                }
            }
        }

        //组装父子结构
        for (TreeMenuDo treeMenuDo : treeMenuDos) {
            TreeMenuDo menu = parentMenus.get(treeMenuDo.getId());
            //如果父级菜单id为空,说明是一级菜单
            if (!"0".equals(menu.getHeadFlag()) && !"0".equals(menu.getSameLevelFlag())) {
                if (menu.getParentId() == null) {
                    if (!treeMenus.contains(menu)) {
                        treeMenus.add(menu);
                    }
                    //移除之前已存在的相同菜单
                    if (new HashSet<>(treeMenus).containsAll(menu.getTreeMenuDos())) {
                        treeMenus.removeAll(menu.getTreeMenuDos());
                    }
                } else {
                    //不是一级菜单, 获取该菜单的父菜单
                    TreeMenuDo parentMenu = parentMenus.get(menu.getParentId());
                    List<TreeMenuDo> menuDos = parentMenu.getTreeMenuDos();
                    if (!menuDos.contains(menu)) {
                        menuDos.add(menu);
                    }
                    //移除之前已存在的相同菜单
                    Set<TreeMenuDo> treeMenuSet = new HashSet<>(treeMenus);
                    if (treeMenuSet.containsAll(menuDos) || treeMenuSet.containsAll(menu.getTreeMenuDos())) {
                        treeMenus.removeAll(menuDos);
                    }
                }
            }
        }

        return treeMenus;
    }

    private List<TreeMenuDo> querySameLevelMenu(TreeMenuDo treeMenuDo, Map<Long, TreeMenuDo> parentMenus) {
        List<TreeMenuDo> treeMenus = new ArrayList<>();
        TreeMenuDo treeMenu = parentMenus.get(treeMenuDo.getNextId()); //找到第一个同级菜单
        while (true) {
            treeMenus.add(treeMenu);
            if (treeMenu.getNextId() == null) {
                break;
            }
            treeMenu = parentMenus.get(treeMenu.getNextId()); //找到到下一个同级菜单
        }
        return treeMenus;
    }

    private TreeMenuDo preCheck(String selectedId) {
        LambdaQueryWrapper<TreeMenuDo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TreeMenuDo::getId, selectedId);
        TreeMenuDo treeMenuDo = treeMenuMapper.selectOne(queryWrapper);
        if (treeMenuDo == null) {
            AssertUtils.isBlank(treeMenuDo, ErrorEnums.ERROR_QUERY.getMessage());
        }
        return treeMenuDo;
    }

    @Override
    public void addSameLevelTreeMenu(AddNodeQuery addNodeQuery) {
        //检查节点是否存在
        TreeMenuDo treeMenuDo = preCheck(addNodeQuery.getSelectedId());

        //获取原来的前一个菜单
        LambdaQueryWrapper<TreeMenuDo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(TreeMenuDo::getId).eq(TreeMenuDo::getNextId, treeMenuDo.getId());
        TreeMenuDo treeMenu = treeMenuMapper.selectOne(queryWrapper);

        //该同级菜单的头节点是否存在
        boolean flag = false;
        TreeMenuDo temp = new TreeMenuDo();
        temp.setId(treeMenuDo.getId());
        while (true) {
            queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.select(TreeMenuDo::getId, TreeMenuDo::getHeadFlag).eq(TreeMenuDo::getNextId, temp.getId());
            TreeMenuDo headTreeMenu = treeMenuMapper.selectOne(queryWrapper);
            if (headTreeMenu == null) {
                break;
            }
            if ("0".equals(headTreeMenu.getHeadFlag())) {
                flag = true;
                break;
            }
            temp.setId(treeMenu.getId());
        }

        //开启事物
        TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);

        try {

            //向上创建菜单
            TreeMenuDo newPreTreeMenuDo =  new TreeMenuDo();
            newPreTreeMenuDo.setDescription(MenuDescription.PRE_MENU);
            newPreTreeMenuDo.setCreateTime(LocalDateTime.now(ZoneId.systemDefault()));
            newPreTreeMenuDo.setNextId(treeMenuDo.getId());
            newPreTreeMenuDo.setSameLevelFlag("0");
            int res0 = treeMenuMapper.insert(newPreTreeMenuDo);
            AssertUtils.isBlank(res0, ErrorEnums.ERROR_CREATE.getMessage());

            //更新原来菜单的nextid
            if (treeMenu != null) {
                LambdaUpdateWrapper<TreeMenuDo> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.set(TreeMenuDo::getNextId, newPreTreeMenuDo.getId())
                        .set(TreeMenuDo::getUpdateTime, LocalDateTime.now(ZoneId.systemDefault()))
                        .eq(TreeMenuDo::getId, treeMenu.getId());
                boolean res1 = this.update(updateWrapper);
                AssertUtils.isBlank(res1, ErrorEnums.ERROR_UPDATE.getMessage());
            }

            //创建头节点
            if (!flag) {
                TreeMenuDo headTreeMenuDo =  new TreeMenuDo();
                headTreeMenuDo.setDescription(MenuDescription.HEAD_MENU);
                headTreeMenuDo.setCreateTime(LocalDateTime.now(ZoneId.systemDefault()));
                headTreeMenuDo.setNextId(newPreTreeMenuDo.getId());
                headTreeMenuDo.setHeadFlag("0");
                int res2 = treeMenuMapper.insert(headTreeMenuDo);
                AssertUtils.isBlank(res2, ErrorEnums.ERROR_CREATE.getMessage());
            }

            //向下创建菜单
            TreeMenuDo newNextTreeMenuDo = new TreeMenuDo();
            newNextTreeMenuDo.setDescription(MenuDescription.NEXT_MENU);
            newNextTreeMenuDo.setCreateTime(LocalDateTime.now(ZoneId.systemDefault()));
            newNextTreeMenuDo.setNextId(treeMenuDo.getNextId());
            newNextTreeMenuDo.setSameLevelFlag("0");
            int res3 = treeMenuMapper.insert(newNextTreeMenuDo);
            AssertUtils.isBlank(res3, ErrorEnums.ERROR_CREATE.getMessage());

            //更新原菜单
            LambdaUpdateWrapper<TreeMenuDo> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(TreeMenuDo::getNextId, newNextTreeMenuDo.getId())
                    .eq(TreeMenuDo::getId, treeMenuDo.getId())
                    .set(TreeMenuDo::getUpdateTime, LocalDateTime.now(ZoneId.systemDefault()));
            boolean res4 = this.update(updateWrapper);
            AssertUtils.isBlank(res4, ErrorEnums.ERROR_UPDATE.getMessage());

            transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            throw new RuntimeException(ErrorEnums.ERROR_CREATE.getMessage());
        }
    }

    @Override
    public void addDifferentLevelTreeMenu(AddNodeQuery addNodeQuery) {
        //检查节点是否存在
        TreeMenuDo treeMenuDo = preCheck(addNodeQuery.getSelectedId());

        //开启事物
        TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);

        try {
            //创建新的父节点
            TreeMenuDo newParentTreeMenuDo =  new TreeMenuDo();
            newParentTreeMenuDo.setDescription(MenuDescription.PARENT_MENU);
            newParentTreeMenuDo.setCreateTime(LocalDateTime.now(ZoneId.systemDefault()));
            int res0 = treeMenuMapper.insert(newParentTreeMenuDo);
            AssertUtils.isBlank(res0, ErrorEnums.ERROR_CREATE.getMessage());

            //更新该节点的父节点
            LambdaUpdateWrapper<TreeMenuDo> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(TreeMenuDo::getParentId, newParentTreeMenuDo.getId())
                    .set(TreeMenuDo::getUpdateTime, LocalDateTime.now(ZoneId.systemDefault()))
                    .eq(TreeMenuDo::getId, addNodeQuery.getSelectedId());
            boolean res1 = this.update(updateWrapper);
            AssertUtils.isBlank(res1, ErrorEnums.ERROR_UPDATE.getMessage());

            //更新父节点指向的父节点
            if (treeMenuDo.getParentId() != null) {
                updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.set(TreeMenuDo::getParentId, treeMenuDo.getParentId())
                        .eq(TreeMenuDo::getId, newParentTreeMenuDo.getId());
                boolean res2 = this.update(updateWrapper);
                AssertUtils.isBlank(res2, ErrorEnums.ERROR_UPDATE.getMessage());
            }

            //创建新的子节点
            TreeMenuDo newChildTreeMenuDo = new TreeMenuDo();
            newChildTreeMenuDo.setParentId(Long.valueOf(addNodeQuery.getSelectedId()));
            newChildTreeMenuDo.setDescription(MenuDescription.CHILD_MENU);
            newChildTreeMenuDo.setCreateTime(LocalDateTime.now(ZoneId.systemDefault()));
            int res3 = treeMenuMapper.insert(newChildTreeMenuDo);
            AssertUtils.isBlank(res3, ErrorEnums.ERROR_CREATE.getMessage());

            transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            throw new RuntimeException(ErrorEnums.ERROR_CREATE.getMessage());
        }
    }


    @Override
    public void copyTreeMenu(CopyNodeQuery copyNodeQuery) {
        List<CopyNodeQuery.CopyNode> copyMenus = copyNodeQuery.getList();
        Map<String, CopyNodeQuery.CopyNode> copyMenusMap
                = copyMenus.stream().collect(Collectors.toMap(CopyNodeQuery.CopyNode::getOriginalId, copyNode -> copyNode));

        //获取父级菜单
        CopyNodeQuery.CopyNode parentTreeMenu = null;
        for (CopyNodeQuery.CopyNode copyMenu : copyMenus) {
            CopyNodeQuery.CopyNode copyNode = copyMenusMap.get(copyMenu.getOriginalParentId());
            if (copyNode == null) {
                parentTreeMenu = copyMenu;
                break;
            }
        }

        //开启事物
        TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);

        try {

            //查看该复制的节点是否存储在上一个同级菜单
            LambdaQueryWrapper<TreeMenuDo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TreeMenuDo::getNextId, parentTreeMenu.getOriginalId());
            TreeMenuDo treeMenuDo = treeMenuMapper.selectOne(queryWrapper);
            if (treeMenuDo != null) {
                //获取中间原菜单节点
                queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.select(TreeMenuDo::getNextId, TreeMenuDo::getParentId).eq(TreeMenuDo::getId, parentTreeMenu.getOriginalId());
                TreeMenuDo menu = treeMenuMapper.selectOne(queryWrapper);

                //更新上一个同级菜单的nextid和parentid
                LambdaUpdateWrapper<TreeMenuDo> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.set(TreeMenuDo::getNextId, menu.getNextId())
                        .set(TreeMenuDo::getParentId, menu.getParentId())
                        .set(TreeMenuDo::getUpdateTime, LocalDateTime.now(ZoneId.systemDefault()))
                        .eq(TreeMenuDo::getId, treeMenuDo.getId());
                boolean res1 = this.update(updateWrapper);
                AssertUtils.isBlank(res1, ErrorEnums.ERROR_UPDATE.getMessage());
            } else {
                //更新原来节点的nextid,并重新排序
                queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(TreeMenuDo::getId, parentTreeMenu.getOriginalId());
                TreeMenuDo treeMenuDo2 = treeMenuMapper.selectOne(queryWrapper);

                queryWrapper = new LambdaQueryWrapper<>();
                Long nextId = treeMenuDo2.getNextId();
                if (nextId != null) {
                    queryWrapper.eq(TreeMenuDo::getNextId, nextId).notIn(TreeMenuDo::getId, treeMenuDo2.getId());
                    TreeMenuDo treeMenuDo3 = treeMenuMapper.selectOne(queryWrapper);
                    if (treeMenuDo3 != null) {
                        LambdaUpdateWrapper<TreeMenuDo> updateWrapper = new LambdaUpdateWrapper<>();
                        updateWrapper.set(TreeMenuDo::getNextId, treeMenuDo2.getId())
                                .set(TreeMenuDo::getParentId, null)
                                .set(TreeMenuDo::getUpdateTime, LocalDateTime.now(ZoneId.systemDefault()))
                                .eq(TreeMenuDo::getId, treeMenuDo3.getId());
                        boolean res = this.update(updateWrapper);
                        AssertUtils.isBlank(res, ErrorEnums.ERROR_UPDATE.getMessage());
                    }
                }
            }

            //更新父菜单的关联上级菜单
            LambdaUpdateWrapper<TreeMenuDo> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(TreeMenuDo::getParentId, Long.valueOf(copyNodeQuery.getSelectedId()))
                    .set(TreeMenuDo::getUpdateTime, LocalDateTime.now(ZoneId.systemDefault()))
                    .eq(TreeMenuDo::getId, parentTreeMenu.getOriginalId());
            boolean res0 = this.update(updateWrapper);
            AssertUtils.isBlank(res0, ErrorEnums.ERROR_UPDATE.getMessage());
            transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            throw new RuntimeException(ErrorEnums.ERROR_CREATE.getMessage());
        }
    }

    @Transactional
    @Override
    public void removeTreeMenu(DeleteNodeQuery deleteNodeQuery) {
        List<String> dis = deleteNodeQuery.getDeleteList();
        AssertUtils.isBlank(dis, ErrorEnums.ERROR_DELETE.getMessage());
        LambdaQueryWrapper<TreeMenuDo> queryWrapper  = new LambdaQueryWrapper<>();
        LambdaUpdateWrapper<TreeMenuDo> updateWrapper;
        for (String id : dis) {
            //查询当前要删除菜单
            queryWrapper.eq(TreeMenuDo::getId, id);
            TreeMenuDo treeMenuDo = treeMenuMapper.selectOne(queryWrapper);
            Long parentId = treeMenuDo.getParentId();
            //如果父id不为空查询子菜单id
            if (parentId != null) {
                //查询子菜单
                queryWrapper  = new LambdaQueryWrapper<>();
                queryWrapper.eq(TreeMenuDo::getParentId, id);
                List<TreeMenuDo> treeMenus = treeMenuMapper.selectList(queryWrapper);
                for (TreeMenuDo treeMenu : treeMenus) {
                    //更新子菜单的parentid和nextid
                    if (treeMenu != null) {
                        updateWrapper = new LambdaUpdateWrapper<>();
                        updateWrapper.set(TreeMenuDo::getParentId, parentId)
                                .set(TreeMenuDo::getNextId, treeMenuDo.getNextId())
                                .set(TreeMenuDo::getUpdateTime, LocalDateTime.now(ZoneId.systemDefault()))
                                .eq(TreeMenuDo::getId, treeMenu.getId());
                        boolean res = this.update(updateWrapper);
                        AssertUtils.isBlank(res, ErrorEnums.ERROR_UPDATE.getMessage());

                        //更新上一个同级菜单的nextid
                        queryWrapper  = new LambdaQueryWrapper<>();
                        queryWrapper.eq(TreeMenuDo::getNextId, id);
                        TreeMenuDo menuDo = treeMenuMapper.selectOne(queryWrapper);
                        if (menuDo != null) {
                            updateWrapper = new LambdaUpdateWrapper<>();
                            updateWrapper.set(TreeMenuDo::getNextId, treeMenu.getId())
                                    .set(TreeMenuDo::getUpdateTime, LocalDateTime.now(ZoneId.systemDefault()))
                                    .eq(TreeMenuDo::getId, menuDo.getId());
                            boolean res1 = this.update(updateWrapper);
                            AssertUtils.isBlank(res1, ErrorEnums.ERROR_UPDATE.getMessage());
                        }
                    }
                }
            }

            //删除该菜单的节点和它的子菜单节点
            boolean res = this.removeById(id);
            AssertUtils.isBlank(res, ErrorEnums.ERROR_DELETE.getMessage());
        }
    }
}
